﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text;
using HtmlAgilityPack;
using log4net;

namespace PlayerStat
{
    public class ScrapePor : ScrapeSite
    {
        ILog logger;
        WebClient wc = new WebClient();
        static int tempNumber = 1;
        private int leagueID = -1;
        private string match_url_template = @"http://www.lpfp.pt/{0}/Pages/Jornada.aspx?epoca={1}{2}&jornada={3}";
        private string league = string.Empty;
        
        public ScrapePor()
        {
            logger = LogManager.GetLogger("scrape");
            if (logger == null)
            {
                throw new Exception("Null Logger");
            }
        }
        
        public void SetLeague(int nLeagueID)
        {
            leagueID = nLeagueID;
            switch (leagueID)
            {
                case 15:
                    league = "liga_zon_sagres";
                    return;
                case 25:
                    league = "liga_orangina";
                    return;
                default:
                    throw new Exception("Wrong League Number");
            }
        }
        
        public string GetMatchURL(int year, int round)
        {
            return string.Format(match_url_template, league,year, year + 1, round);
        }

        public bool Scrape()
        {
            logger.Debug("#Scrape Portgual League# Start");
            HtmlDocument mainDoc = new HtmlDocument();
            HtmlDocument statDoc = new HtmlDocument();
            try
            {
                EventInit evnt = new EventInit(leagueID, 34, Leagues.PortugalID.ToString());
                string date = string.Empty;
                int round = 0;
                int year = 2005;
                string url = string.Empty;
                while (evnt.GetEventRound(leagueID, out year, out round, out date))
                {
                    logger.DebugFormat("Go to season {0}-{1}, round {2}", year, year + 1, round);
                    url = GetMatchURL(year, round);
                    logger.Debug("Download match list.");
                    //wc.DownloadFile(url, @"C:\Scrape\MainIE.html");

                    logger.Debug("Goto match list.");
                    mainDoc.Load(@"C:\Scrape\MainIE.html",Encoding.UTF8);
                    
                    //Get Match Date
                    HtmlNode tbl = null;
                    logger.Debug("Find match tables.");
                    if (!GetMatchTable(mainDoc.DocumentNode, out tbl))
                    {
                        logger.ErrorFormat("The match table is not found. Round({0})" , round);
                        continue;
                    }
                    logger.Debug("Generate datatable of matches.");
                    DataTable matches = null;
                    if (!GetMatchList(tbl, year,date, out matches))
                    {
                        logger.ErrorFormat("Parse the match list of round {0}.", round);
                        continue;
                    }
                    foreach (DataRow dr in matches.Rows)
                    {
                        MatchData md = new MatchData();
                        md.LeagueID = this.leagueID;
                        md.MatchRound = round;
                        md.matchTime = (DateTime)dr["MatchTime"];
                        md.home = (string)dr["HomeTeam"];
                        md.away = (string)dr["AwayTeam"];
                        md.match.homeScore = (int)dr["HomeScore"];
                        md.match.awayScore = (int)dr["AwayScore"];
                        logger.DebugFormat("Scrape the match({0} : {1} ) data.", new string[] { md.home, md.away });
                        if (md.ExistInDB())
                        {
                            md = null;
                            logger.DebugFormat("Data is already scraped into DB.");
                            continue;
                        }
                        logger.Debug("Download match statistic page.");
                        wc.DownloadFile(@"http://www.lpfp.pt" + dr["MatchStatURL"].ToString(), @"C:\Scrape\StatIE.html");
                        logger.Debug("Goto match statistic page.");
                        statDoc.Load(@"C:\Scrape\StatIE.html",Encoding.UTF8);

                        if (!GetMatchStat(statDoc.DocumentNode, ref md))
                        {
                            logger.ErrorFormat("Error to get statistic in {0} vs {1}", md.home, md.away);
                            continue;
                        }
                        logger.Debug("Save match to DB.");
                        md.SaveToDB();
                    }
                    evnt.NextRound();
                }
                logger.Debug("#Scrape Portgual League# End");
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#Scrape Portugal League# Failed: Exception.");
                logger.Error(ex.ToString());
                return false;
            }
            finally
            {
                logger.Debug("#Scrape Portgual League# Finally");
                wc.Dispose();
                mainDoc = null;
                statDoc = null;
            }
        }

        public bool GetMatchTable(HtmlNode doc, out HtmlNode mTable)
        {
            mTable = doc.SelectSingleNode("//table[@id='tg' and @tag='matchday']");
            if (mTable == null)
            {
                logger.ErrorFormat("Failed to get match table.");
                return false;
            }
            else
            {
                return true;
            }
        }
        
        public bool GetMatchList(HtmlNode tbl, int year, string date, out DataTable mTable)
        {
            try
            {
                logger.Debug("#GetMatchList# Start");
                DateTime? edt = null;
                DateTime? idt = null;
                if (!string.IsNullOrEmpty(date))
                {
                    idt = DateTime.Parse(date);
                }
                mTable = new DataTable();
                mTable.Columns.Add("MatchTime", typeof(DateTime));
                mTable.Columns.Add("HomeTeam", typeof(string));
                mTable.Columns.Add("HomeScore", typeof(int));
                mTable.Columns.Add("AwayTeam", typeof(string));
                mTable.Columns.Add("AwayScore", typeof(int));
                mTable.Columns.Add("MatchStatURL", typeof(string));
                HtmlNodeCollection rows = tbl.SelectNodes("./tr[@class='o']");
                if (rows == null)
                {
                    logger.Debug("No match in match table.");
                    return true;
                }
                foreach (HtmlNode tr in rows)
                {
                    edt = Translate.TransPorDateTime(year, tr.ChildNodes[7].InnerText, tr.ChildNodes[9].InnerText);
                    if (idt.HasValue && edt.Value < idt.Value)
                    {
                        continue;
                    }
                    DataRow dr = mTable.NewRow();
                    dr["MatchTime"] = edt.Value;
                    dr["MatchStatURL"] = tr.Attributes["href"].Value;
                    dr["HomeTeam"] = HtmlEntity.DeEntitize(tr.ChildNodes[1].InnerText);
                    dr["AwayTeam"] = HtmlEntity.DeEntitize(tr.ChildNodes[5].InnerText);
                    string[] tempStrs = HtmlEntity.DeEntitize(tr.ChildNodes[3].InnerText).Split('|');
                    if (tempStrs.Length != 2)
                    {
                        throw new Exception("Can not parse match score.");
                    }
                    dr["HomeScore"] = int.Parse(tempStrs[0].Trim());
                    dr["AwayScore"] = int.Parse(tempStrs[1].Trim());
                    mTable.Rows.Add(dr);
                }
                logger.Debug("#GetMatchList# End");
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#GetMatchList# Failed: Exception.");
                logger.Error(ex.ToString());
                mTable = null;
                return false;
            }
        }
        
        public bool GetMatchStat(HtmlNode doc, ref MatchData md)
        {
            try
            {
                logger.Debug("#GetMatchStat# Start");
                HtmlNodeCollection tables = doc.SelectNodes(".//table[@id='teamHome']");
                if (tables == null || tables.Count != 2)
                {
                    logger.Error("The hometeam data is incompleted.");
                    return false;
                }
                foreach (HtmlNode tbl in tables)
                {
                    switch(tbl.SelectSingleNode("./tr[1]").InnerText)
                    {
                        case "Equipa Inicial":
                            //start
                                GetPlayerStat(tbl, true, false,ref md.home, ref md.match, ref md.homeplayers);
                            break;
                        case "Suplentes":
                            //substitution 
                            GetPlayerStat(tbl, true, true,ref md.home, ref md.match, ref md.homeplayers);
                            break;
                    }
                }
                tables = doc.SelectNodes(".//table[@id='teamAway']");
                if (tables == null || tables.Count != 2)
                {
                    logger.Error("The awayteam data is incompleted.");
                    return false;
                }
                foreach (HtmlNode tbl in tables)
                {
                    switch (tbl.SelectSingleNode("./tr[1]").InnerText)
                    {
                        case "Equipa Inicial":
                            //start
                            GetPlayerStat(tbl, false, false, ref md.away, ref md.match, ref md.awayplayers);
                            break;
                        case "Suplentes":
                            //substitution 
                            GetPlayerStat(tbl, false, true, ref md.away, ref md.match, ref md.awayplayers);
                            break;
                    }
                }
                logger.Debug("#GetMatchStat# End");
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#GetMatchStat# Failed: Exception");
                logger.Error(ex.ToString());
                return false;
            }
        }
        
        public bool GetPlayerStat(HtmlNode tbl, bool isHome, bool isSub,ref string team, ref MatchStat match, ref Dictionary<int, PlayerStat> players)
        {
            try
            {
                logger.Debug("#GetPlayerStat# Start");
                HtmlNodeCollection rows = tbl.SelectNodes("./tr[@class='o vt']");
                if (rows == null)
                {
                    logger.DebugFormat("No {0}player data in team ({1}).",isSub?"substitution ":"", team);
                    return false;
                }
                foreach (HtmlNode tr in rows)
                {
                    PlayerStat ps = new PlayerStat();
                    ps.posID = PlayerPos.UnknownID;
                    ps.isPlayerSub = isSub;
                    ps.playerID = GetPlayerID(tr);
                    ps.playerName = HtmlEntity.DeEntitize(tr.ChildNodes[1].InnerText);
                    if (!string.IsNullOrWhiteSpace(HtmlEntity.DeEntitize(tr.ChildNodes[2].InnerText)))
                    {
                      //yellow card
                        if (!GetMinute(tr.ChildNodes[2].InnerText, out ps.yCardMin1))
                        {
                            logger.ErrorFormat("Failed to get yellow card minute of player({0})", ps.playerName);
                        }
                        else
                        {
                            if (isHome)
                            {
                                match.homeYCard = match.homeYCard + 1;
                            }
                            else
                            {
                                match.awayYCard = match.awayYCard + 1;
                            }
                        }
                    }
                    if (!string.IsNullOrWhiteSpace(HtmlEntity.DeEntitize(tr.ChildNodes[3].InnerText)))
                    {
                        //red card
                        if (!GetMinute(tr.ChildNodes[3].InnerText, out ps.redCard))
                        {
                            logger.ErrorFormat("Failed to get red card minute of player({0})", ps.playerName);
                        }
                        else
                        {
                            if (isHome)
                            {
                                match.homeRCard = match.homeRCard + 1;
                            }
                            else
                            {
                                match.awayRCard = match.awayRCard + 1;
                            }
                        }
                    }
                    if (!string.IsNullOrWhiteSpace(HtmlEntity.DeEntitize(tr.ChildNodes[4].InnerText)))
                    {
                        //switch out
                        HtmlNodeCollection images = tr.SelectNodes("./td[5]/img");
                        if (images != null)
                        {
                            foreach(HtmlNode img in images)
                            {
                                string str = img.Attributes["src"].Value;
                                if (str.IndexOf("sub_out.gif") != -1)
                                {
                                    ps.isSwitchOut = true;
                                }
                            }
                        }
                    }
                    if (!string.IsNullOrWhiteSpace(HtmlEntity.DeEntitize(tr.ChildNodes[5].InnerText)))
                    {
                        //goal
                        HtmlNodeCollection divs = tr.SelectNodes("./td[6]/div");
                        if (divs != null)
                        {
                            foreach (HtmlNode div in divs)
                            {
                                MinuteStat ms = new MinuteStat();
                                if (div.InnerText.IndexOf("(ag)") != -1)
                                {
                                    if (!GetMinute(div.InnerText.Replace("(ag)", ""), out ms.goalMin))
                                    {
                                        logger.ErrorFormat("Failed to get goal minute of player({0})", ps.playerName);
                                    }
                                    else
                                    {
                                        ms.isOwnGoal = true;
                                        ps.owngoal = ps.owngoal + 1;
                                        ps.MinStat.Add(ms);
                                    }
                                }
                                else
                                {
                                    if (!GetMinute(div.InnerText, out ms.goalMin))
                                    {
                                        logger.ErrorFormat("Failed to get goal minute of player({0})", ps.playerName);
                                    }
                                    else
                                    {
                                        ms.isOwnGoal = false;
                                        ps.goal = ps.goal + 1;
                                        ps.MinStat.Add(ms);
                                    }
                                }
                            }
                        }
                    }
                    ps.markID = Marking.GetMarkingID(ps.isPlayerSub, ps.isSwitchOut);
                    if (isSub == false || (isSub && !string.IsNullOrWhiteSpace(HtmlEntity.DeEntitize(tr.ChildNodes[4].InnerText))))
                    {
                        players.Add(GenPlayerNumber(), ps);
                    }
                }
                logger.Debug("#GetPlayerStat# End");
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#GetPlayerStat# Failed: Exception");
                logger.Error(ex.ToString());
                return false;
            }
        }
        
        public int GetPlayerID(HtmlNode tr)
        {
            string str = tr.Attributes["href"].Value;
            str = str.Substring(str.IndexOf("jogador=") + "jogador=".Length);
            logger.DebugFormat("Try to parse playerID {0}", str);
            return int.Parse(str);
        }
        
        public bool GetMinute(string str, out int min)
        {
            try
            {
                str = str.Substring(0, str.Length - 1);// remove "'"
                if (str.IndexOf("+") != -1)
                {
                    string[] temp = str.Split('+');
                    min = int.Parse(temp[0]) + int.Parse(temp[1]);
                }
                else
                {
                    min =  int.Parse(str);
                }
                return true;
            }
            catch (Exception ex)
            {
                logger.ErrorFormat("Failed to parse minute {0}", str);
                logger.Error(ex.ToString());
                min = -1;
                return false;
            }
        }
        
        public int GenPlayerNumber()
        {
            return ((tempNumber++) % 30);
        }
    }
}
